/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.bpm.api.runtime.task.impl;

import com.je.bpm.api.runtime.shared.model.impl.ListConverter;
import com.je.bpm.api.runtime.shared.model.impl.ModelConverter;
import com.je.bpm.engine.RepositoryService;
import com.je.bpm.engine.TaskService;
import com.je.bpm.engine.impl.persistence.entity.TaskEntity;
import com.je.bpm.engine.task.IdentityLink;
import com.je.bpm.engine.task.IdentityLinkType;
import com.je.bpm.model.task.model.Task;
import com.je.bpm.model.task.model.impl.TaskImpl;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.util.Collections.emptyList;

public class APITaskConverter extends ListConverter<com.je.bpm.engine.task.Task, Map<String, Object>, Task> implements ModelConverter<com.je.bpm.engine.task.Task,Map<String, Object>, Task> {

    private final TaskService taskService;

    @Autowired
    public APITaskConverter(TaskService taskService) {
        this.taskService = taskService;
    }

    @Override
    public Task from(com.je.bpm.engine.task.Task internalTask) {
        return from(internalTask, calculateStatus(internalTask));
    }

    public Task fromWithCandidates(com.je.bpm.engine.task.Task internalTask) {
        TaskImpl task = buildFromInternalTask(internalTask, calculateStatus(internalTask));
        extractCandidateUsersAndGroups(internalTask, task);
        return task;
    }

    private TaskImpl buildFromInternalTask(com.je.bpm.engine.task.Task internalTask, Task.TaskStatus status) {
        TaskImpl task = new TaskImpl(internalTask.getId(), internalTask.getName(), status);
        task.setProcessDefinitionId(internalTask.getProcessDefinitionId());
        task.setProcessInstanceId(internalTask.getProcessInstanceId());
        task.setAssignee(internalTask.getAssignee());
        task.setClaimedDate(internalTask.getClaimTime());
        task.setCreatedDate(internalTask.getCreateTime());
        task.setDueDate(internalTask.getDueDate());
        task.setDescription(internalTask.getDescription());
        task.setOwner(internalTask.getOwner());
        task.setParentTaskId(internalTask.getParentTaskId());
        task.setPriority(internalTask.getPriority());
        task.setFormKey(internalTask.getFormKey());
        task.setTaskDefinitionKey(internalTask.getTaskDefinitionKey());
        task.setAppVersion(Objects.toString(internalTask.getAppVersion(), null));
        task.setBusinessKey(internalTask.getBusinessKey());

        return task;

    }

    public Task from(com.je.bpm.engine.task.Task internalTask, Task.TaskStatus status) {
        return buildFromInternalTask(internalTask, status);
    }

    public Task fromWithCompletedBy(com.je.bpm.engine.task.Task internalTask, Task.TaskStatus status, String completedBy) {

        TaskImpl task = buildFromInternalTask(internalTask, status);
        task.setCompletedBy(completedBy);

        return task;

    }

    private void extractCandidateUsersAndGroups(com.je.bpm.engine.task.Task source, TaskImpl destination) {
        List<IdentityLink> candidates = taskService.getIdentityLinksForTask(source.getId());
        destination.setCandidateGroups(extractCandidatesBy(candidates, IdentityLink::getGroupId));
        destination.setCandidateUsers(extractCandidatesBy(candidates, IdentityLink::getUserId));
    }

    private List<String> extractCandidatesBy(List<IdentityLink> candidates, Function<IdentityLink, String> extractor) {
        List<String> result = emptyList();
        if (candidates != null) {
            result = candidates
                    .stream()
                    .filter(candidate -> IdentityLinkType.CANDIDATE.equals(candidate.getType()))
                    .map(extractor::apply)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
        }
        return result;
    }

    private Task.TaskStatus calculateStatus(com.je.bpm.engine.task.Task source) {
        if (source instanceof TaskEntity &&
                (((TaskEntity) source).isDeleted() || ((TaskEntity) source).isCanceled())) {
            return Task.TaskStatus.CANCELLED;
        } else if (source.isSuspended()) {
            return Task.TaskStatus.SUSPENDED;
        } else if (source.getAssignee() != null && !source.getAssignee().isEmpty()) {
            return Task.TaskStatus.ASSIGNED;
        }
        return Task.TaskStatus.CREATED;
    }
}
